package com.mashuai.utils.toolkit.core.msInterfaceAuto.scans;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.mashuai.utils.toolkit.core.msInterfaceAuto.annotation.MsEntityScans;
import com.mashuai.utils.toolkit.core.msInterfaceAuto.dto.MsScansClassDto;
import com.mashuai.utils.toolkit.core.msInterfaceAuto.dto.MsScansFieldDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 扫描包实现。
 * ImportBeanDefinitionRegistrar接口此 IOC容器还未完全初始化，因此无法直接获取 bean 实例。
 *
 * @ClassName: scans
 * @Author : mashuai
 * @Description: TODO
 */
@Slf4j
public class MsEntityScansRegistrar implements
        ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    //提示语
    private static final String NO_PACKAGE_PATH = "启动类配置了@MsEntityScans注解，但未发现对应的包名！";
    private static final String PACKAGE_PATH_NO_CLASS = "@MsEntityScans注解中的basePackages包名，未扫描到任何对应的实体类！";

    //@MsScans注解包的名字配置
    private static final String basePackages = "basePackages";
    //资源加载器
    private ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver();
    private final ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
    private final MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    //存放一些缓存
    private List<MsScansClassDto> classDtoList = new CopyOnWriteArrayList<>();

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //从启动类上面获取MapperScan注解
        AnnotationAttributes msScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MsEntityScans.class.getName()));
        if (msScanAttrs == null) return;
        //获取扫描的包名
        String[] packages = msScanAttrs.getStringArray(basePackages);
        if (packages == null || packages.length == 0) {
            log.info(NO_PACKAGE_PATH);
            return;
        }
        Set<Class<?>> allClz = doScan(packages);
        if (allClz.size() == 0) {
            log.info(PACKAGE_PATH_NO_CLASS);
            return;
        }
        //开始动态解析
        long begin = System.currentTimeMillis();
        analysisClz(allClz);
        log.info("@MsEntityScans解析{}个实体类信息，共耗时：{} ms", classDtoList.size(), (System.currentTimeMillis() - begin) / 1000);
    }

    /**
     * 解析class类信息
     **/
    private void analysisClz(Set<Class<?>> allClz) {
        classDtoList.clear();
        for (Class<?> clz : allClz) {
            MsScansClassDto dto = new MsScansClassDto();
            classDtoList.add(dto);
            TableName tableName = clz.getAnnotation(TableName.class);
            dto.setTableName(tableName.value());
            dto.setClz(clz);
            dto.setClassName(clz.getSimpleName());
            dto.setClassPath(clz.getName());
            List<MsScansFieldDto> fieldDtoList = new CopyOnWriteArrayList<>();
            dto.setFiledList(fieldDtoList);
            //遍历字段
            for (Field f : clz.getDeclaredFields()) {
                f.setAccessible(true);
                String fName = f.getName();
                String columnName = f.getName();
                TableField fA = f.getAnnotation(TableField.class);
                if (fA != null) {
                    columnName = fA.value();
                }
                fieldDtoList.add(new MsScansFieldDto(f, columnName, fName, f.getType()));
            }
        }
    }


    /**
     * 扫描指定包下所有包含@TableName的实体类
     *
     * @return
     * @throws IOException
     */
    private Set<Class<?>> doScan(String[] packages) {
        Set<Class<?>> allClass = new HashSet<>();
        for (String pkName : packages) {
            try {
                Set<Class<?>> clzSet = new HashSet<>();
                String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        .concat(ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(pkName))
                                .concat("/**/*.class"));
                Resource[] resources = resolver.getResources(packageSearchPath);
                MetadataReader metadataReader = null;
                for (Resource resource : resources) {
                    if (resource.isReadable()) {
                        metadataReader = metadataReaderFactory.getMetadataReader(resource);
                        try {
                            Class<?> clz = Class.forName(metadataReader.getClassMetadata().getClassName());
                            //只会解析含有@TableName的类
                            if (clz.isAnnotationPresent(TableName.class)) {
                                clzSet.add(clz);
                            }
                        } catch (Exception e) {
                            log.info("在进行@MsEntityScans包扫描时，class类解析错误:" + e.getMessage());
                        }
                    }
                }
                allClass.addAll(clzSet);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return allClass;
    }


//    /**
//     * 获取IMsScansCondition接口的实现类
//     **/
//    public IMsScansCondition getScansCondition() {
//        if (scansCondition == null) {
//            String[] beanNames = MsIocUtils.getBeanNames(IMsScansCondition.class);
//            if (beanNames.length >= 2) {
//                MsAssertUtils.throwInnerException("存在多个IMsScansCondition实现类，请检查配置！");
//            }
//            scansCondition = (IMsScansCondition) MsIocUtils.getBean(beanNames[0], IMsScansCondition.class);
//        }
//        return scansCondition;
//    }

}
